package com.myproject.utils;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 高级URL操作工具类
 * <p>
 * 优化点： 1. 支持多值参数（如?id=1&id=2）[1,2](@ref) 2. 使用URI替代过时的URL类[1](@ref) 3.
 * 预编译正则提升路径处理性能 4. 常量字符集避免重复编解码开销 5. 增强空指针防护和异常分类
 * </p>
 */
public final class UrlUtils {

	private static final Charset UTF_8 = StandardCharsets.UTF_8;
	private static final Pattern MULTI_SLASHES = Pattern.compile("/+");
	private static final Pattern PROTOCOL_SLASHES = Pattern.compile("^([a-z]+:)/+");

	private UrlUtils() {
		throw new AssertionError("Utility class should not be instantiated");
	}

	// ------------------- URL构建 -------------------

	/**
	 * 构建完整URL（支持多值参数）
	 * 
	 * @param protocol 协议（http/https）
	 * @param host     主机名
	 * @param port     端口（-1表示默认端口）
	 * @param path     资源路径
	 * @param params   多值参数Map
	 * @param fragment 锚点
	 * @return 完整URL字符串
	 * @throws URISyntaxException URI语法错误
	 */
	public static String buildUrl(String protocol, String host, int port, String path, Map<String, List<String>> params,
			String fragment) throws URISyntaxException {
		Objects.requireNonNull(protocol, "Protocol must not be null");
		Objects.requireNonNull(host, "Host must not be null");

		path = normalizePath(path);
		String query = encodeQueryParams(params);

		return new URI(protocol, null, host, port, path, query, fragment).toString();
	}

	/**
	 * 路径规范化（处理协议斜杠和重复斜杠）
	 * 
	 * @param path 原始路径
	 * @return 标准化路径
	 */
	public static String normalizePath(String path) {
		if (path == null || path.isEmpty())
			return "/";
		// 修复协议后的多余斜杠（如 http: -> http://）
		path = PROTOCOL_SLASHES.matcher(path).replaceFirst("$1//");
		// 替换非协议部分的多余斜杠
		path = MULTI_SLASHES.matcher(path).replaceAll("/");
		return path.startsWith("/") ? path : "/" + path;
	}

	// ------------------- 参数编码/解码 -------------------

	/**
	 * 编码多值查询参数
	 * 
	 * @param params 参数Map（值可为列表）
	 * @return 编码后的查询字符串
	 */
	public static String encodeQueryParams(Map<String, List<String>> params) {
		if (params == null || params.isEmpty())
			return null;

		return params.entrySet().stream().flatMap(
				entry -> entry.getValue().stream().map(value -> encodeUrl(entry.getKey()) + "=" + encodeUrl(value)))
				.collect(Collectors.joining("&"));
	}

	/**
	 * 解码查询参数为多值Map
	 * 
	 * @param query 查询字符串
	 * @return 多值参数Map
	 */
	public static Map<String, List<String>> decodeQueryParams(String query) {
		Map<String, List<String>> params = new LinkedHashMap<>();
		if (query == null)
			return params;

		Arrays.stream(query.split("&")).map(pair -> pair.split("=", 2)) // 保留空值
				.forEach(parts -> {
					String key = decodeUrl(parts[0]);
					String value = parts.length > 1 ? decodeUrl(parts[1]) : "";
					params.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
				});
		return params;
	}

	/**
	 * URL编码（UTF-8）
	 * 
	 * @param input 原始字符串
	 * @return 编码结果
	 */
	public static String encodeUrl(String input) {
		try {
			return URLEncoder.encode(input, UTF_8.name());
		} catch (UnsupportedEncodingException e) {
			return input; // UTF-8必然支持
		}
	}

	/**
	 * URL解码（UTF-8）
	 * 
	 * @param input 编码字符串
	 * @return 解码结果
	 */
	public static String decodeUrl(String input) {
		try {
			return URLDecoder.decode(input, UTF_8.name());
		} catch (UnsupportedEncodingException e) {
			return input; // UTF-8必然支持
		}
	}

	// ------------------- URL解析 -------------------

	/**
	 * 获取URL协议
	 * 
	 * @param url URL字符串
	 * @return 协议类型
	 * @throws URISyntaxException URI语法错误
	 */
	public static String getProtocol(String url) throws URISyntaxException {
		return new URI(url).getScheme();
	}

	/**
	 * 获取URL主机
	 * 
	 * @param url URL字符串
	 * @return 主机名
	 * @throws URISyntaxException URI语法错误
	 */
	public static String getHost(String url) throws URISyntaxException {
		return new URI(url).getHost();
	}

	// ------------------- 相对路径转换 -------------------

	/**
	 * 转换为相对URL（自动处理锚点和参数）
	 * 
	 * @param baseUrl     基础URL
	 * @param absoluteUrl 绝对URL
	 * @return 相对路径
	 * @throws URISyntaxException URI语法错误
	 */
	public static String toRelativeUrl(String baseUrl, String absoluteUrl) throws URISyntaxException {
		URI base = new URI(baseUrl);
		URI absolute = new URI(absoluteUrl);
		if (!base.getScheme().equals(absolute.getScheme())
				|| !Objects.equals(base.getAuthority(), absolute.getAuthority())) {
			return absoluteUrl; // 协议或主机不同时返回完整URL
		}
		return base.relativize(absolute).toString(); // 标准相对路径计算[1](@ref)
	}
}